﻿using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Routing;
using EPiMVC.Reflection;
using EPiServer;
using EPiServer.Core;

namespace EPiMVC.Routing
{
    internal class PageRouteMapper
    {
        private RequestContext requestContext;
        private RouteConfiguration configuration;
        private List<string> urlSegments;
        private string queryString;
        private PageData page;

        public PageRouteMapper(RequestContext requestContext, RouteConfiguration configuration)
        {
            this.requestContext = requestContext;
            this.configuration = configuration;
        }

        internal void SetRouteValues()
        {
            urlSegments = PathParser.GetUrlSegments(requestContext.HttpContext.Request.RawUrl);
            queryString = PathParser.GetQueryString(requestContext.HttpContext.Request.RawUrl);
            Stack<string> nonPagePathUrlSegments = TryGetPage();
            if (page == null || !page.TypeHasAttribute<MvcPageAttribute>())
                return;

            SetRouteValuesForPage(nonPagePathUrlSegments);
        }

        private Stack<string> TryGetPage()
        {
            Stack<string> nonPagePathUrlSegments = new Stack<string>();
            page = configuration.PageResolver.GetPageByPath(urlSegments.ToPath());
            while (page == null && urlSegments.Count > 0)
            {
                nonPagePathUrlSegments.Push(urlSegments.ElementAt(urlSegments.Count - 1));
                urlSegments.RemoveAt(urlSegments.Count - 1);

                page = configuration.PageResolver.GetPageByPath(urlSegments.ToPath());
            }

            return nonPagePathUrlSegments;
        }

        private string GetDefaultAction()
        {
            if (page.TypeHasAttribute<DefaultActionAttribute>())
                return page.GetTypeAttribute<DefaultActionAttribute>().ActionName;

            return configuration.DefaultActionName;
        }

        private string GetControllerName()
        {
            if (page.TypeHasAttribute<DefaultControllerAttribute>())
                return page.GetTypeAttribute<DefaultControllerAttribute>().ControllerName;

            return configuration.ControllerNameConvention.GetDefaultControllerName(page.GetType());
        }

        private void SetRouteValuesForPage(Stack<string> nonPagePathUrlSegments)
        {
            requestContext.RouteData.Values.Add("controller", GetControllerName());
            requestContext.RouteData.Values.Add(EPiServerRoute.CurrentPageReferenceKey, page.PageLink);

            if(nonPagePathUrlSegments.Count == 0)
            {
                requestContext.RouteData.Values.Add("action", GetDefaultAction());
            }
            else
            {
                SetRouteValuesFromRouteEnding(nonPagePathUrlSegments);
            }
            
            NameValueCollection queryStringParameters = System.Web.HttpUtility.ParseQueryString(queryString);
            for(int i = 0; i < queryStringParameters.Count; i++)
                requestContext.RouteData.Values.Add(queryStringParameters.Keys[i], queryStringParameters[queryStringParameters.Keys[i]]);
        }

        private void SetRouteValuesFromRouteEnding(Stack<string> nonPagePathUrlSegments)
        {
            if (page.TypeHasAttribute<RouteEndingAttribute>())
            {
                SetRouteValuesUsingRouteEndingTemplate(nonPagePathUrlSegments);
            }
            else
            {
                requestContext.RouteData.Values.Add("action", nonPagePathUrlSegments.Pop());
                requestContext.RouteData.Values.Add("parameters", nonPagePathUrlSegments.ToList());
            }
        }

        private void SetRouteValuesUsingRouteEndingTemplate(Stack<string> nonPagePathUrlSegments)
        {
            RouteEndingAttribute routeEndingAttribute = page.GetTypeAttribute<RouteEndingAttribute>();
            List<string> pathNames = PathParser.GetUrlSegments(routeEndingAttribute.RouteUrlEnding);
            while (nonPagePathUrlSegments.Count > 0 && pathNames.Count > 0)
            {
                requestContext.RouteData.Values.Add(pathNames[0], nonPagePathUrlSegments.Pop());
                pathNames.RemoveAt(0);
            }
        }
    }
}
